package com.dongzili.demo.customer.fulltext.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dongzili.demo.common.config.mybatisplus.MybatisPlusUtil;
import com.dongzili.demo.common.config.redis.RedisUtil;
import com.dongzili.demo.common.utils.ReflectUtils;
import com.dongzili.demo.customer.fulltext.analysis.IKAnalyzerSupport;
import com.dongzili.demo.customer.redis.model.Relation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.SetOperations;

import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @描述：
 * @作者： 96511
 * @日期： 2025-01-04 18:35
 */
@Slf4j
public class RedisFullTextService<T> {

    private final static String suffix = "_full_idx";//后缀
    private final Class<T> tClass;
    private final String indexName;
    private final String className;
    private final String primaryColumnKey;
    private final String primaryFieldKey;
    private final List<Field> searchIndexedFields;

    public RedisFullTextService(Class<T> clazz){
        this.tClass = clazz;
        this.className = tClass.getSimpleName();
        this.indexName = this.className+suffix;
        this.primaryColumnKey = ReflectUtils.getPrimaryColumnKey(tClass);
        this.primaryFieldKey = ReflectUtils.getPrimaryFieldKey(tClass);
        this.searchIndexedFields = ReflectUtils.getSearchIndexedFields(tClass);
    }

    /**
     * 保存一条数据，并创建分词索引
     */
    public void save(T entity){

        Object id = ReflectUtils.getPrimaryValue(entity);
        if(id==null){
            return;
        }
        HashOperations<String, String, T> hashOperations1 =  RedisUtil.getHashOperations();
        hashOperations1.put(className, id.toString(), entity);

//        generateIndex(entity, id);
    }

    public void saveAll(List<T> list){
        HashOperations<String, String, T> hashOperations1 =  RedisUtil.getHashOperations();
        Map<String, T> map = list.stream().collect(Collectors.toMap(item -> Objects.requireNonNull(ReflectUtils.getPrimaryValue(item)).toString(), item -> item));
        hashOperations1.putAll(Relation.class.getSimpleName(),map);
        // todo 队列处理
//        generateIndex(entity, id);
    }

    /**
     * 更新分词索引
     */
    public void generateIndex(T entity, Object id) {
        List<String> indexWords = ReflectUtils.getSearchIndexedData(entity);
        if(indexWords==null || indexWords.isEmpty()){
            return;
        }
        SetOperations<String, Object> setOperations =  RedisUtil.getSetOperations();
        for (String text : indexWords){
            try{
                List<String> parts = IKAnalyzerSupport.iKSegmenterToList(text);
                for(String part : parts){
                    String key = indexName+":"+part;
                    setOperations.add(key, id);
                }
            }catch (Exception ignore){

            }
        }
    }
    public void generateIndex(Map<Object, T> map) {
        SetOperations<String, Object> setOperations =  RedisUtil.getSetOperations();
        map.entrySet().parallelStream().forEach(entry -> {
            List<String> indexWords = ReflectUtils.getSearchIndexedData(entry.getValue());
            if(indexWords==null || indexWords.isEmpty()){
                return;
            }
            for (String text : indexWords){
                try{
                    List<String> parts = IKAnalyzerSupport.iKSegmenterToList(text);
                    for(String part : parts){
                        String key = indexName+":"+part;
                        setOperations.add(key, entry.getKey());
                    }
                }catch (Exception ignore){

                }
            }
        });
    }


    /**
     * 根据分词索引查询
     */
    public Page<T> search(String text, Integer currentPage, Integer pageSize){
        List<String> words = IKAnalyzerSupport.iKSegmenterToList(text);
        HashOperations<String, String, T> hashOperations1 =  RedisUtil.getHashOperations();
        SetOperations<String, Object> setOperations =  RedisUtil.getSetOperations();
        List<String> setKeys = words.stream().map(word -> indexName+":"+word).collect(Collectors.toList());
//        Set<Object> idSet = setOperations.union(setKeys); //并集
        Set<Object> idSet = setOperations.intersect(setKeys);//交集
        List<Object> idList = new ArrayList<>(Objects.requireNonNull(idSet));
        List<String> strList = idList.stream().map(Object::toString).collect(Collectors.toList());

        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        queryWrapper.in(primaryColumnKey, strList);
        queryWrapper.orderByDesc(primaryColumnKey);
        return MybatisPlusUtil.getMapper(tClass).selectPage(new Page<>(currentPage, pageSize), queryWrapper);
    }

//    public void syncIndex2() throws InterruptedException {
//        int current = 1;
//        int size = 2000;
//        LocalDateTime start = LocalDateTime.now();
//        log.info("同步索引启动，当前页：{}，每页大小：{}", current, size);
//        LambdaQueryWrapper<Relation> queryWrapper = new LambdaQueryWrapper<>();
//        queryWrapper.orderByDesc(Relation::getId);
//        Page<Relation> page = MybatisPlusUtil.getMapper(Relation.class).selectPage(new Page<>(current, size), queryWrapper);
//        List<Relation> list = page.getRecords();
//        for (Relation relation : list){
//            RedisFullTextUtils.getService(Relation.class).save(relation);
//        }
//        log.info("同步索引开始，当前页：{}，每页大小：{}，耗时：{}", current, size, ChronoUnit.MILLIS.between(start, LocalDateTime.now()));
//        ExecutorService pool = ExecutorUtils.getPool();
//        pool.execute(()->{
//            long pages = page.getPages();
//            for (int i = 2; i <= pages; i++){
//                LambdaQueryWrapper<Relation> wrapper = new LambdaQueryWrapper<>();
//                wrapper.orderByDesc(Relation::getId);
//                String lastLimit = " limit "+(i-1)*size+","+size;
//                wrapper.last(lastLimit);
//                List<Relation> currentList = MybatisPlusUtil.getMapper(Relation.class).selectList(wrapper);
//                for (Relation relation : currentList){
//                    RedisFullTextUtils.getService(Relation.class).save(relation);
//                }
//                log.info("同步索引中...，当前页：{}，每页大小：{}，耗时：{}", i, size, ChronoUnit.MILLIS.between(start, LocalDateTime.now()));
//            }
//        });
//        pool.shutdown();
//        pool.awaitTermination(6, TimeUnit.HOURS);
//        log.info("同步索引结束，当前页：{}，每页大小：{}，耗时：{}", current, size, ChronoUnit.MILLIS.between(start, LocalDateTime.now()));
//
//    }

    public void syncIndex() throws InterruptedException {
        int current = 1;
        int size = 5000;
        LocalDateTime start = LocalDateTime.now();

        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc(primaryColumnKey);
        BaseMapper<T> mapper = MybatisPlusUtil.getMapper(tClass);
        Long total = mapper.selectCount(new LambdaQueryWrapper<>());
        long pages = (long) Math.ceil((double) total /size);

        log.info("同步索引启动，总共：{} 行，每页大小：{}, 总页数：{}", total, size, pages);
        log.info("同步索引开始，当前页：{}，每页大小：{}，耗时：{}", current, size, ChronoUnit.MILLIS.between(start, LocalDateTime.now()));

//        ExecutorService pool = ExecutorUtils.getPool();
//        for (int i = 1; i <= pages; i++){
//            int finalI = i;
//            pool.execute(()->{
//                try{
//                    doSync(finalI, mapper, size, start);
//                }catch (Exception ex){
//                    log.error("同步索引失败，当前页：{}，每页大小：{}", finalI, size, ex);
//                }
//
//            });
//            log.info("同步索引中...，当前页：{}，每页大小：{}，耗时：{}", i, size, ChronoUnit.MILLIS.between(start, LocalDateTime.now()));
//        }
////        pool.shutdown();
//        boolean termination = pool.awaitTermination(6, TimeUnit.HOURS);
//        if (!termination) {
//            log.warn("线程池未能在指定时间内完成所有任务，部分任务可能没有执行完");
//        }
        log.info("同步索引结束，当前页：{}，每页大小：{}，耗时：{}", current, size, ChronoUnit.MILLIS.between(start, LocalDateTime.now()));
    }

    private void doSync(int finalI,BaseMapper<T> mapper, int size, LocalDateTime start) {
        LocalDateTime currentStart = LocalDateTime.now();
        QueryWrapper<T> wrapper = new QueryWrapper<>();
        wrapper.orderByDesc(primaryColumnKey);
        String lastLimit = " limit "+(finalI -1)* size +","+ size;
        wrapper.last(lastLimit);
        List<T> currentList = mapper.selectList(wrapper);
        Map<Object, T> map = currentList.stream().collect(Collectors.toMap(ReflectUtils::getPrimaryValue, t -> t));
        generateIndex(map);
        log.info("同步索引中...，当前页：{}，每页大小：{}，此页耗时：{}秒, 持续耗时：{}秒", finalI, size, ChronoUnit.SECONDS.between(currentStart, LocalDateTime.now()), ChronoUnit.SECONDS.between(start, LocalDateTime.now()));
        currentList = null;
        wrapper = null;
    }


    /**
     * 删除索引
     * @param id
     */
    public void deleteIndex(Object id) {
        HashOperations<String, String, Object> hashOperations1 =  RedisUtil.getHashOperations();
        hashOperations1.delete(className, id.toString());
        SetOperations<String, Object> setOperations =  RedisUtil.getSetOperations();
        List<String> indexWords = ReflectUtils.getSearchIndexedData(id);
        if(indexWords==null || indexWords.isEmpty()){
            return;
        }
        for (String text : indexWords){
            try{
                List<String> parts = IKAnalyzerSupport.iKSegmenterToList(text);
                for(String part : parts){
                    String key = indexName+":"+part;
                    setOperations.remove(key, id);
                }
            }catch (Exception ignore){

            }
        }
    }


}
